#include "FreeRTOSConfig.h"
#include "projdefs.h"
#include "task.h"
#include "list.h"

/*
*************************************************************************
*                               宏函数
*************************************************************************
*/

/* 查找最高优先级的就绪任务：通用方法 */
#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 )
  
  /* uxTopReadyPriority 是一个在 task.c 中定义的静态变量, 用于表示创建任务的最高优先级, 默认初始化为 0, 即空闲任务的优先级 */
  #define taskRECORD_READY_PRIORITY( uxPriority )\
  {\
    if( ( uxPriority ) > uxTopReadyPriority )\
    {\
      uxTopReadyPriority = (uxPriority); \
    }\
  } /* taskRECORD_READY_PRIORITY */

/*-----------------------------------------------------------*/
  
  /* 用于寻找优先级最高的就绪任务，实质就是更新 uxTopReadyPriority 和 pxCurrentTCB的值。 */
  #define taskSELECT_HIGHEST_PRIORITY_TASK()\
  {\
    UBaseType_t uxTopPriority = uxTopReadyPriority;\
		\
    /* 寻找包含就绪任务的最高优先级的队列 */\
    while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) )\
    {\
      --uxTopPriority;\
    }\
    \
    /* 获取优先级最高的就绪任务的 TCB，然后更新到 pxCurrentTCB */\
    listGET_OWNER_OF_NEXT_ENTRY(pxCurrentTCB, &(pxReadyTasksLists[ uxTopPriority ]));\
    \
    /* 更新  uxTopReadyPriority */\
    uxTopReadyPriority = uxTopPriority;\
  } /* taskSELECT_HIGHEST_PRIORITY_TASK */
  
/*-----------------------------------------------------------*/

/* 这两个宏定义只有在选择优化方法时才用，这里定义为空 */
//#define taskRESET_READY_PRIORITY( uxPriority ) 
//#define portRESET_READY_PRIORITY( uxPriority, uxTopReadyPriority ) 

#else /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
  
  /*
    taskRECORD_READY_PRIORITY()用于根据传进来的形参(通常形参就是任务的优先级), 将变量的  uxTopReadyPriority 的某个位置 1。
    与通用方法中用来表示创建的任务的最高优先级不一样，它在优化方法中担任的是一个优先级位图表的角色, ，即该变量的每个位对应任务
    的优先级，如果任务就绪，则将对应的位置1，反之清零，因此只需要计算出 uxTopReadyPriority 的前导 0 的个数, 就能找到就绪任务的
    最高优先级。
  */
  #define taskRECORD_READY_PRIORITY( uxPriority ) \
          portRECORD_READY_PRIORITY( uxPriority, uxTopReadyPriority )

  /*
    得益于 Cortex-M 内核提供了 CLZ 前导零计算指令, 可以很快的根据 uxTopReadyPriority 计算出
    最高优先级任务在就绪队列当中处于的下标位置.
  */
  #define taskSELECT_HIGHEST_PRIORITY_TASK()\
  {\
    UBaseType_t uxTopPriority;\
    \
    /* 从优先级位图中寻找最高优先级, port.c 当中关于硬件的实现 */\
    portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );\
    \
    /* 获取优先级最高的就绪任务的 TCB，然后更新到 pxCurrentTCB */\
    listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );\
  } /* taskSELECT_HIGHEST_PRIORITY_TASK() */
  
  /*-----------------------------------------------------------*/
  #if 0
    #define taskRESET_READY_PRIORITY( uxPriority )\
    {\
      if(listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[( uxPriority)]))==(UBaseType_t)0)\
      {\
        portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) );\
      }\
    }
  #else
    #define taskRESET_READY_PRIORITY( uxPriority )\
    {\
      portRESET_READY_PRIORITY((uxPriority ), (uxTopReadyPriority));\
    }
  #endif

#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */

/* 宏函数, 将任务添加到就绪队列当中, 由 prvAddNewTaskToReadyList 进行调用*/
#define prvAddTaskToReadyList( pxTCB )\
  /* 记录一下最高优先级 */\
  taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );\
  \
   /* 将任务添加到就绪列表对应优先级位置链表的末尾, 注意：这里这样写是因为暂时只考虑节点上只挂载一个任务节点的情况*/\
  vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ),\
                  &( ( pxTCB )->xStateListItem ) );

/*
*************************************************************************
*                               全局变量
*************************************************************************
*/

/* 当前正在运行的任务的任务控制块指针，默认初始化为NULL */
TCB_t * volatile pxCurrentTCB = NULL;
List_t pxReadyTasksLists[ configMAX_PRIORITIES ];

/*
*************************************************************************
*                               私有全局变量
*************************************************************************
*/
static volatile TickType_t xTickCount 				      = ( TickType_t ) 0U;
static TaskHandle_t xIdleTaskHandle					        = NULL;
static volatile UBaseType_t uxCurrentNumberOfTasks 	= ( UBaseType_t ) 0U;

/* FreeRTOS 定义了两个列表, 当系统时基计数器 xTickCount 没有溢出时, 用一条列表, 当溢出时, 用另外一条列表 */
static List_t xDelayedTaskList1;
static List_t xDelayedTaskList2;

/* 任务延时列表指针, 指向没有溢出那条列表 */
static List_t * volatile pxDelayedTaskList; 

/* 任务延时列表指针, 指向溢出使用的那条列表 */
static List_t * volatile pxOverflowDelayedTaskList;

/* 用于表示下一个任务解锁的 ticks */
static volatile TickType_t xNextTaskUnblockTime		= ( TickType_t ) 0U;

/* 系统解锁 ticks 溢出次数 */
static volatile BaseType_t xNumOfOverflows 			= ( BaseType_t ) 0;

/*
若使用软件方式计算就绪列表中最高优先级: 则 uxTopReadyPriority 记录的就是就绪任务中的最高优先级
若使用硬件方式计算就绪列表中最高优先级: 则 uxTopReadyPriority 记录的是就绪任务优先级的位图
*/
static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY;

/*
*************************************************************************
*                                函数声明
*************************************************************************
*/

static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB );
static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait );
static void prvResetNextTaskUnblockTime( void );

// 就绪列表初始化
void prvInitialiseTaskLists( void )
{
	/* 任务优先级 */
	UBaseType_t uxPriority;

	for ( uxPriority = ( UBaseType_t ) 0U;
		  uxPriority < ( UBaseType_t ) configMAX_PRIORITIES;
		  uxPriority++ )
	{
		// 依次初始化对应列表项的数组
		vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
	}
	
	/* 初始化延时任务列表 */
	vListInitialise( &xDelayedTaskList1 );
	vListInitialise( &xDelayedTaskList2 );
	
	/* 指定溢出 和 非溢出延时列表指针的指向 */
	pxDelayedTaskList = &xDelayedTaskList1;
	pxOverflowDelayedTaskList = &xDelayedTaskList2;
}

static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,            /* 任务入口，即任务的函数名称 */
                                  const char * const pcName,            /* 任务名称, 字符串形式 */
								                  const uint32_t ulStackDepth,          /* 任务栈大小, 单位为字 */
								                  void * const pvParameters,            /* 任务形参 */
																	UBaseType_t uxPriority,               /* 任务优先级 */
								                  TaskHandle_t * const pxCreatedTask,   /* 任务句柄, void ** 二级指针 */
								                  TCB_t *pxNewTCB)                      /* 任务控制块指针。 */
{
	/* 栈顶指针 */
	StackType_t *pxTopOfStack;

	UBaseType_t x;

	/* 获取栈顶指针: 栈顶从提供的数组的末尾开始, 这样满足栈地址递减的特性*/
	pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );

	/* 栈内存对齐: 8 字节, & 上 0x1000 , 若后三位有数字, 则抛弃掉这样就是 8 的倍数*/
    /* 32位系统中 4 字节对即可，但是希望支持浮点运算, 因此使用 8 字节对齐 */
	pxTopOfStack = ( StackType_t * )( ( ( uint32_t ) pxTopOfStack ) & ( ~( ( uint32_t ) 0x0007 ) ) );

	/* 任务名存储在 TCB 中 */
	for ( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
	{
		pxNewTCB->pcTaskName[ x ] = pcName[ x ];
		if( pcName[x] == 0 )
		{
			break;
		}
	}
	pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';

	/* 初始化 TCB 中的 钩子 节点: 其实就是让节点钩子的 container 指针置空 */
	vListInitialiseItem( &( pxNewTCB->xStateListItem ) );

	/* 设置 TCB 中的钩子节点 container 指针指向 TCB 本身  */
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
	
	/* 任务优先级初始化 */
	if ( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
	{
		uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
	}
	pxNewTCB->uxPriority = uxPriority;
	

	/* 初始化任务栈: 向任务战的前面部分写入固定内容 如 任务函数的地址、任务形参地址, 添加完毕后栈顶指针需要更新*/
	pxNewTCB->pxTopOfStack = pxPortInitialiseStack(pxTopOfStack, pxTaskCode, pvParameters);

	/* 让任务句柄指向任务控制块: 任务栈未传入空的即可创建成功*/	
	if ( ( void * ) pxCreatedTask != NULL )
	{
		*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
	}
}

#if ( configSUPPORT_STATIC_ALLOCATION == 1 )

TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,            /* 任务入口，即任务的函数名称 */
                                const char * const pcName,            /* 任务名称, 字符串形式 */
								                const uint32_t ulStackDepth,          /* 任务栈大小, 单位为字 */
								                void * const pvParameters,            /* 任务形参 */
																UBaseType_t uxPriority,               /* 任务优先级, 数字越大, 优先级越高*/
								                StackType_t * const puxStackBuffer,   /* 任务栈起始地址 */
								                TCB_t * const pxTaskBuffer)           /* 任务控制块指针。 */
{
	TCB_t *pxNewTCB;
	
	/* 返回的资源句柄 */
	TaskHandle_t xReturn;

	/* 用户传递进来的 PCB 指针 和 堆栈指针不能为空 */
	if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) )
	{
		pxNewTCB = ( TCB_t * ) pxTaskBuffer;
		
		/* 关联 TCB 和 任务栈 */
		pxNewTCB->pxStack = (StackType_t *)puxStackBuffer;

		/* 创建新的任务:  内部关联 句柄 和 TCB */
		prvInitialiseNewTask( pxTaskCode,
		                      pcName,
							            ulStackDepth,
							            pvParameters,
		                      uxPriority,
							            &xReturn,
							            pxNewTCB);
		
		/* 将任务添加到就绪队列 */
		prvAddNewTaskToReadyList( pxNewTCB );
	}
	else
	{
		xReturn = NULL;
	}

	/* 创建 xReturn 则指向任务 TCB */
	return xReturn;
}

#endif /* configSUPPORT_STATIC_ALLOCATION */

// 空闲任务体
static portTASK_FUNCTION( prvIdleTask, pvParameters )
{
	/* 防止编译器的警告 */
	( void ) pvParameters;
    
    for(;;)
    {
        /* 空闲任务暂时什么都不做 */
    }
}

void vTaskStartScheduler( void )
{
  /*======================= 创建空闲任务 start=======================*/
	
	/* 创建空闲任务 */
  TCB_t *pxIdleTaskTCBBuffer = NULL;         /*  用于指向空闲任务控制块 */
  StackType_t *pxIdleTaskStackBuffer = NULL; /*  用于空闲任务栈起始地址 */
  uint32_t ulIdleTaskStackSize;
	
	/*  获取空闲任务的内存：任务栈和任务 TCB */
  vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer,
                                 &pxIdleTaskStackBuffer,
                                 &ulIdleTaskStackSize );
	
  /*  创建 空闲任务 */
  xIdleTaskHandle = 
  xTaskCreateStatic( (TaskFunction_t)prvIdleTask,           /*  任务入口 */
                     (char *)"IDLE",                        /*  任务名称，字符串形式 */
                     (uint32_t)ulIdleTaskStackSize ,        /*  任务栈大小，单位为字 */
                     (void *) NULL,                         /*  任务形参 */
											(UBaseType_t) tskIDLE_PRIORITY,       /*  空闲任务优先级 */
                     (StackType_t *)pxIdleTaskStackBuffer,  /*  任务栈起始地址 */
                     (TCB_t *)pxIdleTaskTCBBuffer );        /*  任务控制块 */
	
  /* 将任务添加到就绪列表 */
  // vListInsertEnd( &( pxReadyTasksLists[0] ), &( ((TCB_t *)pxIdleTaskTCBBuffer)->xStateListItem ) );

  /*========================== 创建空闲任务 end=====================*/
		
  /* 初始化指定 下个任务 解锁 ticks 为 portMAX_DELAY */
  xNextTaskUnblockTime = portMAX_DELAY;										 
	 
	/* 手动指定第一个运行的任务, 目前不支持优先级, 先暂时指定一个任务作为第一个任务运行 */
	// pxCurrentTCB = &Task1TCB;

	/* 启动调度器 */
	if ( xPortStartScheduler() != pdFALSE )
	{
		/* 调度器启动成功，则不会返回，即不会来到这里 */	
	}
}

void vTaskDelay( const TickType_t xTicksToDelay )
{
  TCB_t *pxTCB = NULL;
  
  /* 获取当前任务的 TCB , 明确一点, 能调用 vTaskDelay 函数的任务一定是当前正在运行的任务*/
  pxTCB = pxCurrentTCB;
	
	/* 将任务插入到延时队列 */
	prvAddCurrentTaskToDelayedList( xTicksToDelay );
  
  /* 延时设置完毕, 则此任务直接放弃 CPU 的时间 */
  taskYIELD();
}

// 将指定任务插入到延时队列中
static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait )
{
	TickType_t xTimeToWake;
	
	/* 当前运行到的 ticks */
	const TickType_t xConstTickCount = xTickCount;

	/* 将任务从就绪列表中移除, 列表中移除 且 优先级位图清0 */
  if ( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
  {
    portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
  }
	
	/* 计算当前任务解锁 ticks, 并设置到任务钩子节点的 value中, 用于后续排序*/
	xTimeToWake = xConstTickCount + xTicksToWait;
	listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );
	
	/* 当前任务解锁 ticks 溢出, 则插入到溢出链表中 */
	if( xTimeToWake < xConstTickCount )
	{
		vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
	}
	
	/* 没有溢出则插入到未溢出链表 */
	else
	{
		vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
	}
	
	/* 若当前任务的 ticks 更先解锁 */
	if( xTimeToWake < xNextTaskUnblockTime )
	{
		xNextTaskUnblockTime = xTimeToWake;
	}
}

// 重置解锁 ticks:
// 1. 若延时队列为空, 则 解锁 ticks 设置为最大.
// 2. 若延时队列不为空, 则 解锁 ticks 设置为链表第一个节点的 xItemValue
static void prvResetNextTaskUnblockTime( void )
{
	TCB_t *pxTCB;
	if ( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
  {
    xNextTaskUnblockTime = portMAX_DELAY;
  }
	else
	{
		( pxTCB ) = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
		xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB )->xStateListItem ) );
	}
}

// 延时队列切换: 系统时基变量的溢出作为延时队列切换的时机
#define taskSWITCH_DELAYED_LISTS()\
{\
	/* 交换 */\
  List_t *pxTemp;\
	pxTemp = pxDelayedTaskList;\
	pxDelayedTaskList = pxOverflowDelayedTaskList;\
	pxOverflowDelayedTaskList = pxTemp;\
	/* 溢出计数++ */\
	xNumOfOverflows++;\
	/* 重置解锁时间 */\
	prvResetNextTaskUnblockTime();\
}

// 更新系统时基
void xTaskIncrementTick( void )
{
  TCB_t *pxTCB = NULL;
  TickType_t xItemValue;
	
	const TickType_t xConstTickCount = xTickCount + 1;
	xTickCount = xConstTickCount;
	
	/* 如果 系统ticks 溢出, 则切换延时队列 */
	if( xConstTickCount == ( TickType_t ) 0U )
  {
		taskSWITCH_DELAYED_LISTS();
	}
	
	/* 解锁 ticks 到达 */
  if ( xConstTickCount >= xNextTaskUnblockTime )
	{
		for(;;)
		{
			/* 延时列表已经被删空, 则 xNextTaskUnblockTime 的值设置为可能的最大值 */
			if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
			{
				xNextTaskUnblockTime = portMAX_DELAY;
				break;
			}
			
			/* 延时队列中还有任务 */
			else
			{
				/* 获取延时队列的第一个任务 及 其延时到期值 */
				pxTCB = ( TCB_t * )  listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
				xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB ->xStateListItem ) );
				
				/* 延时列表首任务的 解锁ticks > 系统维护的解锁 ticks, 则没有任务在延时中了 */
				if ( xConstTickCount < xItemValue )
        {
          xNextTaskUnblockTime = xItemValue;
          break;
        }
				
				/* 删除第一个任务*/
				( void ) uxListRemove( &( pxTCB->xStateListItem ) );
				
				 /* 将解除等待的任务添加到就绪列表 */
				 prvAddTaskToReadyList( pxTCB );
			}
		}
	} /* xConstTickCount >= xNextTaskUnblockTime */
		
	// 每次 systick 中断触发一次任务切换
	portYIELD();
}

/* 将 TCB 增加到就绪列表的函数 */
static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
	/* 进入临界段 */
  taskENTER_CRITICAL();
	{
		/* 记录任务数量 + 1*/
		uxCurrentNumberOfTasks++;
		if ( pxCurrentTCB == NULL )
		{
			pxCurrentTCB = pxNewTCB;
			
			/* 第一次创建任务, 则要初始化任务列表*/
			if(uxCurrentNumberOfTasks == ( UBaseType_t )1)
			{
				 prvInitialiseTaskLists();
			}
		}
		
		/* 如果 pxCurrentTCB 不为空，则根据任务的优先级将 pxCurrentTCB 指向最高优先级任务的 TCB  */
		else
		{
			if ( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
			{
				pxCurrentTCB = pxNewTCB;
			}
		}
		
		/* 将任务添加到就绪列表 */
		prvAddTaskToReadyList( pxNewTCB );
	}
	/* 退出临界段 */
	taskEXIT_CRITICAL();
}

#if 1

  /* 任务切换，即寻找优先级最高的就绪任务 */
  void vTaskSwitchContext( void )
  {
    /*  获取优先级最高的就绪任务的 TCB ，然后更新到 pxCurrentTCB */
    taskSELECT_HIGHEST_PRIORITY_TASK();
  }

#else

void vTaskSwitchContext( void )
{
	
	// 如果当前是空闲任务, 则尝试切换到任务 1 和 任务2, 前提是两者无延时, 否则依然保持运行空闲任务
	if ( pxCurrentTCB == &IdleTaskTCB )
  {
    if ( Task1TCB.xTicksToDelay == 0 )
    {
      pxCurrentTCB =&Task1TCB;
    }
    else if( Task2TCB.xTicksToDelay == 0 )
    {
      pxCurrentTCB =&Task2TCB;
    }
    else
    {
      return;
    }
  }
	
	// 非空闲任务
	else
	{
		// 当前任务是1, 任务2无延则切换到任务2
		// 任务 2 和 当前正在运行的任务1都有延时, 则切换到 空闲任务.
    if( pxCurrentTCB == &Task1TCB )
    {
      if( Task2TCB.xTicksToDelay == 0 )
      {
        pxCurrentTCB = &Task2TCB;
      }
      else if( pxCurrentTCB->xTicksToDelay != 0 )
      {
        pxCurrentTCB = &IdleTaskTCB;
      }
      else
      {
        /* 返回, 不进行切换, 因为两个任务都处在延时状态 */
        return;
      }
    }
		
		// 当前任务是2, 任务1无延则切换到任务1
		// 任务 1 和 当前正在运行的任务2都有延时, 则切换到 空闲任务.
		else if( pxCurrentTCB == &Task2TCB )
    {
      if( Task1TCB.xTicksToDelay == 0 )
      {
        pxCurrentTCB =&Task1TCB;
      }
      else if( pxCurrentTCB->xTicksToDelay != 0 )
      {
        pxCurrentTCB = &IdleTaskTCB;
      }
      else
      {
        /* 返回, 不进行切换, 因为两个任务都处在延时状态 */
        return;
      }
    }
	}
}

#endif

